#include "comLib\VideoDecoder.h"
#include<assert.h>
#include<stb_image_write.h>

CVideoDecoder::CVideoDecoder(const AVCodecID id,const AVPixelFormat pixFormat)
{
	m_pPkt= av_packet_alloc();
	av_init_packet(m_pPkt);
	if (!m_pPkt) {
		assert(false);
	}
	AVCodec *codec = avcodec_find_decoder(id);
	if (!codec) {
		fprintf(stderr, "Codec not found\n");
		exit(1);
	}

	m_pCodecParContext = av_parser_init(codec->id);
	if (!m_pCodecParContext) {
		fprintf(stderr, "parser not found\n");
		exit(1);
	}

	m_pCodecContext = avcodec_alloc_context3(codec);
	if (!m_pCodecContext) {
		fprintf(stderr, "Could not allocate video codec context\n");
		exit(1);
	}
	m_pCodecContext->pix_fmt = pixFormat;

	if (avcodec_open2(m_pCodecContext, codec, NULL) < 0) {
		fprintf(stderr, "Could not open codec\n");
		exit(1);
	}

	m_pFrame = av_frame_alloc();
	if (!m_pFrame) {
		fprintf(stderr, "Could not allocate video frame\n");
		exit(1);
	}
}


CVideoDecoder::~CVideoDecoder()
{
}

AVFrame *CVideoDecoder::decode(uint8_t *data,int data_size)
{
	char buf[1024];
	int ret;

	AVPacket *pkt = av_packet_alloc();

	while (data_size>0){
		ret = av_parser_parse2(m_pCodecParContext, m_pCodecContext, &pkt->data, &pkt->size,
			data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
		if (ret < 0) {
			fprintf(stderr, "Error while parsing\n");
			exit(1);
		}

		data += ret;
		data_size -= ret;

		if (pkt->size)
		{
			decodeParsePkt(pkt,nullptr);
		}
	}
	av_packet_free(&pkt);

	return 0;
}

int CVideoDecoder::decodePkt(AVPacket *pkt1, const FrameCallback &frameCb)
{
	char buf[1024];
	int ret, data_size;
	uint8_t *data;

	AVPacket *pkt = av_packet_alloc();
	data = pkt1->data;
	data_size = pkt1->size;

	while (data_size>0){
		ret = av_parser_parse2(m_pCodecParContext, m_pCodecContext, &pkt->data, &pkt->size,
			data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
		if (ret < 0) {
			fprintf(stderr, "Error while parsing\n");
			assert(false);
			exit(1);
		}

		data += ret;
		data_size -= ret;

		if (pkt->size)
		{
			decodeParsePkt(pkt, frameCb);
		}
	}
	av_packet_free(&pkt);

	return 0;
}

int CVideoDecoder::decodeParsePkt(const AVPacket *pkt, const FrameCallback &frameCb)
{
	char buf[1024];
	int ret = 0;

	ret = avcodec_send_packet(m_pCodecContext, pkt);
	if (ret < 0) {
		av_strerror(ret, buf, 1024);
		fprintf(stderr, "Error sending a packet for decoding\n");
		exit(1);
	}

	while (ret >= 0) {
		ret = avcodec_receive_frame(m_pCodecContext, m_pFrame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
			av_strerror(ret, buf, 1024);
			return ret;
		}
		else if (ret < 0) {
			av_strerror(ret, buf, 1024);
			fprintf(stderr, "Error during decoding\n");
			exit(1);
		}

		/*uint8_t *dst_data[4];
		int dst_linesize[4];
		if ((ret = av_image_alloc(dst_data, dst_linesize,
			m_pFrame->width, m_pFrame->height, AV_PIX_FMT_RGBA, 4)) < 0) {
			printf("Could not allocate destination image\n");
		}

		m_pSwsContext = sws_getContext(m_pFrame->width, m_pFrame->height, AV_PIX_FMT_YUV420P,
			m_pFrame->width, m_pFrame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
		ret = sws_scale(m_pSwsContext,
			(uint8_t **)(m_pFrame->data), m_pFrame->linesize, 0, m_pFrame->height,
			dst_data, dst_linesize);*/
		frameCb(m_pFrame);
		//stbi_write_png("dec.png", m_pFrame->width, m_pFrame->height, 4, dst_data[0], dst_linesize[0]);
	}
}